package com.youlu.campus.service.rank.impl;

import com.alibaba.fastjson.JSON;
import com.youlu.campus.service.rank.RankBaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.Set;

/**
 * 排行榜实现
 * 参考 ：https://blog.csdn.net/m0_37459380/article/details/82971525
 */
@Slf4j
@Service
public class RankBaseServiceImpl implements RankBaseService {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public void addAll(String key, Set<ZSetOperations.TypedTuple<String>> tuples) {
        log.info(":>>> 开始批量添加排行榜:{},{}", key, JSON.toJSON(tuples));
        Long num = redisTemplate.opsForZSet().add(key, tuples);
        log.info(":>>> 批量添加排行榜数量:{},{}", key, num);
    }

    @Override
    public Set<ZSetOperations.TypedTuple<String>> getRankDatas(String key, Integer start, Integer end) {
//        Set<String> range = redisTemplate.opsForZSet().reverseRange(key, start, end);
        Set<ZSetOperations.TypedTuple<String>> rangeWithScores = redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
        return rangeWithScores;
    }

    @Override
    public void addSingleRank(String key, String oKey, Double score) {
        boolean r = redisTemplate.opsForZSet().add(key, oKey, score);
        log.info(":>>>  添加:{} 子Key:{} 分数:{} 结果为:{}", key, oKey, score, r);
    }

    @Override
    public Long getSingleRankOrder(String key, String oKey) {
        Long rankNum = redisTemplate.opsForZSet().reverseRank(key, oKey);
        if (Objects.nonNull(rankNum)) {
            rankNum = rankNum + 1;
        }
        log.info(":>>> 排行榜:{},子Key:{} 名次:{}", key, oKey, rankNum);
        return rankNum;
    }

    @Override
    public Double getSingleRankScore(String key, String oKey) {
        Double score = redisTemplate.opsForZSet().score(key, oKey);
        log.info(":>>> 排行榜:{},子Key:{} 分数:{}", key, oKey, score);
        return score;
    }

    @Override
    public Long getRankTotal(String key) {
        Long total = redisTemplate.opsForZSet().zCard(key);
        log.info(":>>> 排行榜:{},大小:{}", key, total);
        return total;
    }

    @Override
    public Double increRankScore(String key, String oKey, Double score) {
        Double scoreAfter = redisTemplate.opsForZSet().incrementScore(key, oKey, score);
        log.info(":>>> 累加排行榜:{},子Key:{} ,分数:{},结果为:{}", key, key, oKey, score, scoreAfter);
        return scoreAfter;
    }

    @Override
    public ZSetOperations.TypedTuple<String> getMax(String key) {
        Set<ZSetOperations.TypedTuple<String>> re = getRankDatas(key, 0, 1);
        if (Objects.isNull(re)) {
            return null;
        }
        ZSetOperations.TypedTuple<String> ree = null;
        for (ZSetOperations.TypedTuple<String> r : re) {
            ree = r;
            break;
        }
        log.info(":>>> 排行榜:{} 最大值:{}", key, JSON.toJSON(ree));
        return ree;
    }

    @Override
    public ZSetOperations.TypedTuple<String> getMin(String key) {
        Long total = getRankTotal(key);
        if (total.compareTo(0L) != 1) {
            return null;
        }
        Long start = total - 1L;
        Set<ZSetOperations.TypedTuple<String>> re = getRankDatas(key, start.intValue(), total.intValue());
        if (Objects.isNull(re)) {
            return null;
        }
        ZSetOperations.TypedTuple<String> ree = null;
        for (ZSetOperations.TypedTuple<String> r : re) {
            ree = r;
        }
        log.info(":>>> 排行榜:{} 最小值:{}", key, JSON.toJSON(ree));
        return ree;
    }

    @Override
    public boolean remove(String key, String oKey) {
        Long re = redisTemplate.opsForZSet().remove(key, oKey);
        log.info(":>>> 删除排行榜:{},{},结果:{}", key, oKey, re);
        if (re.compareTo(0L) == 1) {
            return true;
        }
        return false;
    }
}
